#ifndef __BBTCP_H__
#define __BBTCP_H__
#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <vector>
#include <thread>
#include <mutex>
#include <queue>
#include <map>
#include "MsgPack.h"
#include "linkedlist.h"
#include "nfevents.h"

#define ICOP_CLIENT 0x01
#define IOCP_SERVICE_IPV4 0x02
#define IOCP_SERVICE_IPV6 0x04
#define IOCP_ALL (ICOP_CLIENT | IOCP_SERVICE_IPV4 | IOCP_SERVICE_IPV6)

class ELP_API IOCPService
{
public:
    IOCPService();
    ~IOCPService();
    void destory();
    STATUS init(int mode = IOCP_ALL, int port = 2023);
    STATUS sendMsg(ENDPOINT_ID id, const char *buf, int len);
    STATUS close(ENDPOINT_ID id);
    STATUS setEventHandler(TCPEventHandler *pEventHandler);
    int getPort();
    bool isIPFamilyAvailable(int ipFamily);
    bool setPrivate(ENDPOINT_ID eID, LPVOID pData);
    ENDPOINT_ID connect(const char *szIp, int port, int family = AF_INET, LPVOID pData = NULL);
    LPVOID getPrivate(ENDPOINT_ID eID);

private:
    void onSendComplete(SOCKET socket, DWORD dwTransferred, OV_DATA *pov, int error);
    void onReceiveComplete(SOCKET socket, DWORD dwTransferred, OV_DATA *pov, int error);
    void onAcceptComplete(SOCKET socket, DWORD dwTransferred, OV_DATA *pov, int error);
    void addAcceptWithOK(OV_DATA* pov);
    void onConnectComplete(SOCKET socket, DWORD dwTransferred, OV_DATA *pov, int error);
    void onClose(SOCKET socket, DWORD dwTransferred, OV_DATA* pov, int error);

    bool initExtensions();
    void *getExtensionFunction(SOCKET s, const GUID *which_fn);
    void dispatch(SOCKET socket, DWORD dwTransferred, OVERLAPPED *pOverlapped, int error);
    void setKeepAliveVals(SOCKET s, SOCKET listenSocket = INVALID_SOCKET);
    void execute();
    bool postCompletion(SOCKET s, DWORD dwTransferred, LPOVERLAPPED pol);
    bool registerSocket(SOCKET s);
    void workerThread();
    void addConnect(ENDPOINT_ID eID, LPVOID pData = NULL);
    bool startClose(SOCKET socket);
    bool startReceive(ENDPOINT_ID id, SOCKET socket);
    bool startAccept(ENDPOINT_ID id);
    bool startConnect(SOCKET socket, sockaddr *pAddr, int addrLen, ENDPOINT_ID id);
    OV_DATA* newOV_DATA();
    void deleteOV_DATA(OV_DATA* pov);
    STATUS failExit();

private:
    std::atomic<bool> m_stop = false;
    HANDLE m_hIOCP;
    std::mutex m_ConnectLock;
    std::map<ENDPOINT_ID, LPVOID> m_connectlist;
    std::mutex m_taskSyncLock;
    std::condition_variable cv_task;
    std::vector<std::thread *> m_threads;
    std::queue<POV> tasks;

    SOCKET m_listenSocket;
    SOCKET m_listenSocket_IPv6;
    bool m_ipv4Available;
    bool m_ipv6Available;
    int m_port;
    LPFN_ACCEPTEX m_pAcceptEx;
    LPFN_CONNECTEX m_pConnectEx;
    LPFN_GETACCEPTEXSOCKADDRS m_pGetAcceptExSockaddrs;
    TCPEventHandler *m_pPFEventHandler;
    std::mutex m_ovDataLock;
    LIST_ENTRY	m_ovDataList;
    DWORD m_ovDataCounter;
};
#endif // !__BBTCP_H__